package ligne2vie.topos.client.forms;

import ligne2vie.topos.shared.bo.CoordinatesConversion;
import ligne2vie.topos.shared.bo.TMapPoint;
import ligne2vie.topos.shared.bo.TSummit;

import com.google.gwt.maps.client.base.HasLatLng;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * The class {@link SummitEditForm} provides a complete form to add or edit information about a summit. It is directly
 * embeddable into a widget and all its methods allows controlling it from anywhere.
 * 
 * @author thuguerre
 * @since 1.0.0
 */
public class SummitEditForm extends VerticalPanel {

	private TextBox tbName;
	private TextBox tbAltitude;
	private TextBox tbLatitude;
	private TextBox tbLongitude;
	private TextArea taDescription;

	private double latitude;
	private double longitude;

	/**
	 * Default constructor.
	 * 
	 * @param okButton
	 *            The button which validates and processes action when the form is filled. Up to the instantiating
	 *            process to provide it due to very specific action.
	 * @param cancelButton
	 *            The button which cancels the form and the action. Up to the instantiating process to provide it due to
	 *            very specific action.
	 */
	public SummitEditForm(final Button okButton, final Button cancelButton) {

		// --- Creating all requested inputs

		FlexTable mainPanel = new FlexTable();
		mainPanel.setCellSpacing(6);

		mainPanel.setWidget(0, 0, new Label("Nom : "));
		tbName = new TextBox();
		tbName.setText("");
		mainPanel.setWidget(0, 1, tbName);

		mainPanel.setWidget(1, 0, new HTML("Altitude (m&egrave;tres) : "));
		tbAltitude = new TextBox();
		tbAltitude.setText("");
		mainPanel.setWidget(1, 1, tbAltitude);

		mainPanel.setWidget(2, 0, new Label("Latitude : "));
		tbLatitude = new TextBox();
		tbLatitude.setReadOnly(true);
		mainPanel.setWidget(2, 1, tbLatitude);

		mainPanel.setWidget(3, 0, new Label("Longitude : "));
		tbLongitude = new TextBox();
		tbLongitude.setText("");
		tbLongitude.setReadOnly(true);
		mainPanel.setWidget(3, 1, tbLongitude);

		mainPanel.setWidget(4, 0, new Label("Description : "));

		taDescription = new TextArea();
		taDescription.setCharacterWidth(32);
		taDescription.setVisibleLines(8);

		// --- Creating the final button panel

		HorizontalPanel buttonPanel = new HorizontalPanel();
		buttonPanel.add(cancelButton);
		buttonPanel.add(okButton);

		// --- Filling the panel

		this.add(new HTML("<p>Remplisser les informations principales (nom et altitude), puis cliquez sur OK.</p>"));
		this.add(mainPanel);
		this.add(taDescription);
		this.setHorizontalAlignment(ALIGN_RIGHT);
		this.add(buttonPanel);

		this.focus();
	}

	/**
	 * The method {@link #focus()} set the focus on the first text box the user has to fill : the summit name.
	 */
	public void focus() {
		this.tbName.setFocus(true);
	}

	public int getAltitude() {
		return Integer.valueOf(this.tbAltitude.getText().trim()).intValue();
	}

	public String getDescription() {
		return this.taDescription.getText().trim().substring(0, 499);
	}

	public double getLatitude() {
		return this.latitude;
	}

	public double getLongitude() {
		return this.longitude;
	}

	public String getName() {
		return this.tbName.getText().trim();
	}

	public void setAltitude(int altitude) {
		this.tbAltitude.setText(String.valueOf(altitude));
	}

	public void setDescription(String description) {
		this.taDescription.setText(description);
	}

	public void setLatitude(double latitude) {
		this.latitude = latitude;
		this.tbLatitude.setText(CoordinatesConversion.fromLatitude(latitude).toString());
	}

	public void setLongitude(double longitude) {
		this.longitude = longitude;
		this.tbLongitude.setText(CoordinatesConversion.fromLongitude(longitude).toString());
	}

	public void setName(String name) {
		this.tbName.setText(name);
	}

	/**
	 * The method {@link #setPosition(HasLatLng)} allows setting easily the summit position using a common structure. It
	 * calls both methods {@link #setLatitude(double)} and {@link #setLongitude(double)} with corresponding values.
	 * 
	 * @param position
	 *            Summit position to display.
	 */
	public void setPosition(HasLatLng position) {

		if (position != null) {
			this.setLatitude(position.getLatitude());
			this.setLongitude(position.getLongitude());
		}
	}

	/**
	 * Convenience method allowing filling current form with all information provided from received summit. By safety,
	 * all corresponding setters are called.
	 * 
	 * @param summit
	 *            Summit to fill the form with.
	 */
	public void setSummit(TSummit summit) {

		if (summit != null) {

			this.setName(summit.getName());
			this.setAltitude(summit.getAltitude());
			this.setLatitude(summit.getLatitude());
			this.setLongitude(summit.getLongitude());
			this.setDescription(summit.getDescription());
		}
	}

	/**
	 * The method {@link #validate()} validates the form verifying all user information which should have been filled :
	 * <ul>
	 * <li>Summit name : cannot be empty,</li>
	 * <li>Summit altitude : cannot be empty, negative or upper than Mount Everest.</li>
	 * </ul>
	 * 
	 * @return <code>true</code> if all conditions are verified, <code>false</code> else.
	 */
	public boolean validate() {

		final String INVALID_CSS_STYLE = "invalid";

		// --- removing useless characters

		tbName.getText().trim();
		tbAltitude.getText().trim();

		// --- removing invalid CSS style from inputs

		tbName.removeStyleDependentName(INVALID_CSS_STYLE);
		tbAltitude.removeStyleDependentName(INVALID_CSS_STYLE);

		// --- starting verification

		boolean valid = true;

		if (!TMapPoint.isNameValid(this.getName())) {

			tbName.addStyleDependentName(INVALID_CSS_STYLE);
			valid = false;
		}

		if (!TMapPoint.isAltitudeValid(tbAltitude.getText())) {

			tbAltitude.addStyleDependentName(INVALID_CSS_STYLE);
			valid = false;
		}

		return valid;
	}
}